package com.icee.myth.deamon;

import com.icee.myth.common.AbstractHeartbeatClient;
import com.icee.myth.common.encoder.Type2BytesLengthFieldProtobufEncoder;
import com.icee.myth.common.message.serverMessage.InternalChannelMessage;
import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.deamon.channelHandler.DeamonToManagerHandler;
import com.icee.myth.deamon.channelHandler.builder.DeamonMessageBuilder;
import com.icee.myth.protobuf.builder.DeamonToManagerBuilder;
import com.icee.myth.utils.LogConsts;
import com.icee.myth.utils.MLogger;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import java.net.InetSocketAddress;
import java.util.concurrent.LinkedTransferQueue;

/**
 * Deamon服务器作为客户端去连接Manager服务器
 *
 * @author liuxianke
 */
public class DeamonToManagerClient extends AbstractHeartbeatClient {

    //Singleton
    private static DeamonToManagerClient INSTANCE = new DeamonToManagerClient();

    //getInstance操作的第一次调用在Main的startOnlyOnce中，因此无需同步
    public static DeamonToManagerClient getInstance() {
        return INSTANCE;
    }

    private DeamonToManagerClient() {
    }

    @Override
    public void init(String host, int port, final boolean deamonToManager, final int managerToClusterServerId, LinkedTransferQueue<Message> queue) {
        this.queue = queue;
        this.host = host;
        this.port = port;

        this.bootstrap = new Bootstrap();
        bootstrap.group(new NioEventLoopGroup());
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.remoteAddress(new InetSocketAddress(host, port));
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch)
                    throws Exception {
                ch.pipeline().addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 2, 4, 0, 0));
                // deamonToManager
                ch.pipeline().addLast("deamonToManagerEncoder", new Type2BytesLengthFieldProtobufEncoder());   //customEncoder
                ch.pipeline().addLast("deamonToManagerHandler", new DeamonToManagerHandler());
            }
        });
    }

    @Override
    protected Object buildClientHeartBeat() {
        return DeamonToManagerBuilder.buildDeamonHeartbeat();
    }

    @Override
    protected Message buildServerDownMessage() {
        return DeamonMessageBuilder.buildManagerDownMessage();
    }

    @Override
    protected void serverDownWarning() {
        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Manager is down!");
    }

    @Override
    protected void serverCloseWarning() {
        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Manager closed!");
    }

    public void handleMessage(Message message) {
        switch (message.getType()) {
            case ALL_HEARTBEAT: {
                heartbeat();
                break;
            }
            case DEAMON_MANAGERHEARTBEAT: {
                // 对方有心跳，重置心跳计数
                restoreHeartbeat();
                break;
            }
            case DEAMON_MANAGERCONNECT: {
                // 处理ClusterConnect消息
                serverConnect(((InternalChannelMessage) message).channel);
//                // 发送当前GW id给Cluster
//                channelContext.write(GWToClusterBuilder.buildRegister(GWServer.getInstance().getGWId(),GWServer.getInstance().getPlayers()));
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Manager connected!");
                break;
            }
            case DEAMON_MANAGERCLOSE: {
                // 处理ClusterClose消息
                serverClose();
                // 重新连接cluster server，每次尝试隔1秒
                reconnect();
                break;
            }
        }
    }

}
